#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <cstring>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1024;
char cmd_line[N];
char* g_argv[64];    // 用于储存分割后的命令
// 写一个环境变量buffer
char g_myval[N];
// shell运行原理： 通过让子进程执行命令，父进程等待&&解析命令
int main()
{
  // 命令行解释器一定是一个常住内存的解释器，不退出
  while(true)
  {
    // 1. 打印出提示信息
    cout<<"[root@localhost myshell]# ";
    fflush(stdout);   // 需要手动刷新缓冲区 
    // 2. 获取用户的键盘输入
    fgets(cmd_line, sizeof cmd_line, stdin);
    cmd_line[strlen(cmd_line)-1]='\0';
    // 3. 命令字符串解析
    g_argv[0]=strtok(cmd_line, " ");
    int i=1;    // g_argv[N]的下标 
    if(strcmp(g_argv[0], "ls") == 0)
    {
      g_argv[i++]=(char*)"--color=auto";
    }
    else if(strcmp(g_argv[0], "ll")==0)
    {
      g_argv[0]=(char*)"ls";
      g_argv[i++]=(char*)"-l";
      g_argv[i++]=(char*)"--color=auto";
    }
    while(g_argv[i++]=strtok(NULL," "));
    if(strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
    {
      strcpy(g_myval, g_argv[1]);
      putenv(g_myval);
      continue;
    }
    // 4.内置命令，让父进程（shell)自己执行的命令，叫做内置命令或者内建命令
    if(strcmp(g_argv[0], "cd") == 0)
    {
      if(g_argv[1] != NULL) chdir(g_argv[1]);
    }
    
    // 5.fork()
    pid_t id = fork();
    if(id == 0)
    {
      // child
      cout<<"下面的功能是子进程执行的" << endl;
      // 当执行cd命令(cd ..)时，只会影响子进程的当前目录，不会影响父进程的路径，所以有第四步
      execvp(g_argv[0], g_argv);
      exit(66);
    }
    // father
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret > 0)
      cout<<"退出码："<<WEXITSTATUS(status)<<endl;
  }
  return 0;
}
